Skip to content

feat: implement secret primitive#29

Merged
sourcehawk merged 27 commits intomainfrom
feature/secret-primitive
Mar 25, 2026
Merged

feat: implement secret primitive#29
sourcehawk merged 27 commits intomainfrom
feature/secret-primitive

Conversation

@sourcehawk
Copy link
Owner

@sourcehawk sourcehawk commented Mar 22, 2026

Implements the secret Kubernetes resource primitive following the pattern established by the existing ConfigMap and Deployment primitives.

Summary

  • Adds secret primitive package under pkg/primitives/secret/
  • Implements required lifecycle interfaces
  • Includes editors, mutator, flavors, and builder
  • Adds SecretDataEditor to shared pkg/mutation/editors/ (follows existing ConfigMapDataEditor pattern)
  • Updates shared documentation (docs/primitives.md) to include the new primitive in the index

Checklist

  • Compiles cleanly
  • Tests pass
  • Follows naming conventions in CONTEXT.md
  • Shared file modifications are intentional and minimal (documentation index + new editor)

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new secret primitive to the operator component framework, mirroring the existing static primitives (e.g., configmap) with builder/resource APIs, mutation + flavor pipelines, hashing utilities, tests, docs, and an end-to-end example.

Changes:

  • Introduces pkg/primitives/secret (resource, builder, mutator, flavors, and hash utilities) with comprehensive unit tests.
  • Adds a new shared editor SecretDataEditor under pkg/mutation/editors to support typed .data / .stringData mutations.
  • Adds documentation for the secret primitive plus a runnable examples/secret-primitive showcasing composition via feature-gated mutations and flavors.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pkg/primitives/secret/resource.go Implements Secret Resource wrapper around internal generic static resource.
pkg/primitives/secret/resource_test.go Tests identity/object deep-copy/mutation/data extraction behavior.
pkg/primitives/secret/mutator.go Implements plan-and-apply mutator API for Secret metadata and data edits.
pkg/primitives/secret/mutator_test.go Tests mutator editing helpers, ordering, and interface implementation.
pkg/primitives/secret/hash.go Adds DataHash and Resource.DesiredHash utilities.
pkg/primitives/secret/hash_test.go Tests determinism/sensitivity and DesiredHash behavior.
pkg/primitives/secret/flavors.go Adds Secret-specific field application flavors incl. preserving external .data entries.
pkg/primitives/secret/flavors_test.go Tests flavors and builder integration with Mutate.
pkg/primitives/secret/builder.go Adds fluent builder API over internal generic static builder.
pkg/primitives/secret/builder_test.go Tests builder validation and registration behavior.
pkg/mutation/editors/secretdata.go Adds a shared typed editor for Secret .data and .stringData.
pkg/mutation/editors/secretdata_test.go Tests SecretDataEditor behavior and nil-handling.
examples/secret-primitive/resources/secret.go Example resource factory wiring mutations/flavors/extractors.
examples/secret-primitive/README.md Example documentation and run instructions.
examples/secret-primitive/main.go Runnable example using fake client + multiple reconciliation steps.
examples/secret-primitive/features/mutations.go Example feature-gated Secret mutations using SetStringData + metadata edits.
examples/secret-primitive/features/flavors.go Example flavor wrapper for preserving external entries.
examples/secret-primitive/app/controller.go Example controller wiring component + Secret resource.
docs/primitives/secret.md New user-facing documentation for the secret primitive (builder, ordering, flavors, hashing).

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • examples/secret-primitive/resources/secret.go: Data extractor no longer prints secret values. Now prints only key names and value lengths, with a comment warning against logging secret values in production controllers.

Intentionally ignored:

  • docs/primitives/secret.md table format: The comment claims the capabilities table uses double leading/trailing pipes, but the actual file already uses standard single-pipe | Capability | Detail | format, consistent with docs/primitives/configmap.md. No change needed.

Copilot AI review requested due to automatic review settings March 22, 2026 19:48
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • Added pkg/primitives/secret (Static) to the Built-in Primitives table in docs/primitives.md
  • Added SecretDataEditor to the Mutation Editors table in docs/primitives.md

Intentionally ignored:
None

<!-- claude-review-cycle -->

@sourcehawk
Copy link
Owner Author

approved

Copilot AI review requested due to automatic review settings March 23, 2026 03:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • Updated PR description/checklist to acknowledge shared file modifications (docs/primitives.md index update and new SecretDataEditor in pkg/mutation/editors/). Replaced misleading "Does not modify shared files" checkbox with accurate description of intentional, minimal shared file changes.

Intentionally ignored:
None

<!-- claude-review-cycle -->

Copilot AI review requested due to automatic review settings March 23, 2026 16:43
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • docs/primitives/secret.md:122 — Narrowed ordering guarantee language to describe per-mutation edit ordering rather than claiming cross-feature boundaries. Also updated the Apply() doc comment in mutator.go to match.

Intentionally ignored:

  • pkg/primitives/secret/mutator_test.go:146 (remove beginFeature() test) — This is a valid unit test of the mutator's internal plan-splitting logic. The configmap primitive has an identical test (TestMutator_MultipleFeatures). Removing it would leave the beginFeature() method untested while keeping the method itself.
  • pkg/primitives/secret/mutator.go:50 (remove beginFeature() / feature plan infrastructure) — This is a framework-level concern: the sealed FeatureMutator interface affects all primitives equally (deployment, configmap, and secret all have the same pattern). Removing the infrastructure from secret alone would create inconsistency. The fix belongs in the framework (exporting the boundary hook), not in individual primitives.

<!-- claude-review-cycle -->

sourcehawk and others added 10 commits March 23, 2026 20:17
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, and hash

Implements the Secret primitive following the same pattern as the ConfigMap
primitive. Includes full test coverage for builder validation, mutator
operations, field application flavors, and data hashing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Demonstrates building a Secret resource with base credentials, version
labels, and feature-gated tracing/metrics tokens using the secret primitive.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nd example

- hash.go: Merge .stringData into a copy of .data before hashing to match
  Kubernetes API-server write semantics. Ensures DesiredHash reflects content
  set via SetStringData.
- flavors.go: PreserveExternalEntries now treats keys present in
  applied.StringData as owned, preventing incorrect preservation of cluster
  values that the operator intends to overwrite via .stringData.
- secret.md: Add nil-check for current.Data in the custom field applicator
  example to prevent panic. Update DataHash documentation to describe the
  merged hash semantics.
- example secret.go: Remove misleading StringData iteration from the data
  extractor since .stringData is write-only and never returned by the API
  server on read.
- Add tests for StringData-aware hash and flavor behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Print only key names and value lengths instead of base64-encoded values
to prevent credential leakage if the example is copied into production.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Secret to the Built-in Primitives table and SecretDataEditor
to the Mutation Editors table in docs/primitives.md so the new
primitive is discoverable from the main index page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Aligns with the fix applied to deployment and configmap mutators in #42.
The constructor now initializes the plans slice directly instead of calling
beginFeature(), preventing an empty feature plan when mutate_helper.go
calls fm.beginFeature() before each mutation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 25, 2026 16:16
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Comment on lines +117 to +124
| Editor | Scope |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| `ContainerEditor` | Environment variables, arguments, resource limits, ports |
| `PodSpecEditor` | Volumes, tolerations, node selectors, service account, security context |
| `DeploymentSpecEditor` | Replicas, update strategy, label selectors |
| `ConfigMapDataEditor` | `.data` entries — set, remove, deep-merge YAML patches, raw access |
| `SecretDataEditor` | `.data` and `.stringData` — set/remove bytes, `SetString`/`RemoveString`, `Raw()`/`RawStringData()` |
| `ObjectMetaEditor` | Labels and annotations on any Kubernetes object |
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These rows use || which is not valid GitHub-flavored Markdown table syntax (it creates an empty first column and typically won’t render as intended). Consider switching this to a standard Markdown table using single leading pipes (e.g., | Editor | Scope |) to ensure proper rendering.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +13
| Capability | Detail |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| **Static lifecycle** | No health tracking, grace periods, or suspension — the resource is reconciled to desired state |
| **Mutation pipeline** | Typed editors for `.data` and `.stringData` entries and object metadata, with a raw escape hatch |
| **Data extraction** | Reads generated or updated values back from the reconciled Secret after each sync cycle |
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capabilities table uses || which does not render as a standard Markdown table in GitHub-flavored Markdown (it generally introduces an unintended empty first column). Update to conventional table syntax (leading | per row) so the documentation renders correctly.

Copilot uses AI. Check for mistakes.
@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:
None

Intentionally ignored:

  • docs/primitives.md line 124 — Copilot flagged || double-pipe table syntax, but the table already uses correct single-pipe | GFM syntax. No || occurrences exist in the file. This appears to be a stale comment from a prior revision that was already fixed in commit 0941c3b.
  • docs/primitives/secret.md line 13 — Same issue: Copilot flagged || table syntax, but the file already uses correct single-pipe formatting with no || present.

@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 25, 2026 16:25
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Comment on lines +70 to +72
func (e *SecretDataEditor) Remove(key string) {
delete(*e.data, key)
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove/RemoveString dereference e.data / e.stringData without guarding against a nil pointer receiver or a zero-value SecretDataEditor (where these fields are nil), which will panic. Since SecretDataEditor is exported, callers can construct it without NewSecretDataEditor; consider making the type resilient by guarding e == nil and e.data == nil / e.stringData == nil (no-op), or make the struct unexported to enforce construction via NewSecretDataEditor.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +88
func (e *SecretDataEditor) RemoveString(key string) {
delete(*e.stringData, key)
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove/RemoveString dereference e.data / e.stringData without guarding against a nil pointer receiver or a zero-value SecretDataEditor (where these fields are nil), which will panic. Since SecretDataEditor is exported, callers can construct it without NewSecretDataEditor; consider making the type resilient by guarding e == nil and e.data == nil / e.stringData == nil (no-op), or make the struct unexported to enforce construction via NewSecretDataEditor.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +20
Mutate: func(m *secret.Mutator) error {
m.SetStringData("username", "app-user")
m.SetStringData("password", "default-password")
return nil
},
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example bakes credential-like values directly into source (password/token strings). Even for examples, this is a risky pattern to normalize—consider replacing these with clearly non-sensitive placeholders (e.g. \"REPLACE_ME\"), generating random values at runtime for the demo, or reading from environment/flags, and add an explicit comment that these must never be used as-is in real controllers.

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +49
Mutate: func(m *secret.Mutator) error {
m.SetStringData("otel-auth-token", "trace-secret-token-abc123")
return nil
},
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example bakes credential-like values directly into source (password/token strings). Even for examples, this is a risky pattern to normalize—consider replacing these with clearly non-sensitive placeholders (e.g. \"REPLACE_ME\"), generating random values at runtime for the demo, or reading from environment/flags, and add an explicit comment that these must never be used as-is in real controllers.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +62
Mutate: func(m *secret.Mutator) error {
m.SetStringData("metrics-auth-token", "metrics-secret-token-xyz789")
return nil
},
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example bakes credential-like values directly into source (password/token strings). Even for examples, this is a risky pattern to normalize—consider replacing these with clearly non-sensitive placeholders (e.g. \"REPLACE_ME\"), generating random values at runtime for the demo, or reading from environment/flags, and add an explicit comment that these must never be used as-is in real controllers.

Copilot uses AI. Check for mistakes.
…dentials in examples

Add nil-map guards to SecretDataEditor.Remove and RemoveString so they
are no-ops when the underlying map is nil, preventing panics on
zero-value structs. Replace hard-coded credential-like strings in the
example mutations with "REPLACE_ME" placeholders and add a doc comment
warning against hard-coding secrets in real controllers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • SecretDataEditor.Remove nil-map guard (secretdata.go:72): Added nil check for *e.data before calling delete, making Remove a no-op when the underlying map is nil.
  • SecretDataEditor.RemoveString nil-map guard (secretdata.go:88): Added nil check for *e.stringData before calling delete, same defensive pattern.
  • Example hard-coded credentials (mutations.go:20,49,62): Replaced credential-like values ("default-password", "trace-secret-token-abc123", "metrics-secret-token-xyz789") with "REPLACE_ME" placeholders and added a doc comment warning against hard-coding secrets in real controllers.

Intentionally ignored:
None

<!-- claude-review-cycle -->

@sourcehawk sourcehawk merged commit 3bc2dab into main Mar 25, 2026
2 checks passed
@sourcehawk sourcehawk deleted the feature/secret-primitive branch March 25, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants